/*
 * Copyright (c) 2012 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.libermundi.theorcs.chronicles.model.sheet;

import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table;
import javax.persistence.Transient;

import org.libermundi.theorcs.chronicles.ChronicleConstants;
import org.libermundi.theorcs.chronicles.json.SheetExlusionStrategy;
import org.libermundi.theorcs.chronicles.json.annotation.NoJson;
import org.libermundi.theorcs.core.model.base.NumericIdEntity;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

@Entity(name="SheetPage")
@Table(name=ChronicleConstants.TBL_SHEETPAGE)
public final class SheetPage extends NumericIdEntity {
	private static final Logger logger = LoggerFactory.getLogger(SheetPage.class);
	
	public static final String PROP_SERIALIZED_PAGE="page";
	public static final String PROP_NAME="name";
	
	private static final long serialVersionUID = -6091917575680123914L;

	public final AtomicBoolean isDeserializeRequired=new AtomicBoolean(true);

	@NoJson
	private String _serializedPage;
	
	@NoJson
	private Map<String,SheetContainer> _cachedContainer = Maps.newHashMap();
	
	@NoJson
	private String _name;
	
	/*
	 * Here only in order to trigger the saving when using Hibernate
	 */
	//TODO : A vérifier.... j'ai un doute
	@NoJson
	private Date _timestamp;
	
	private List<SheetContainer> _containers=Lists.newArrayList();
	
	public SheetPage() {
		//TODO : Localize
		this("Page 1");
	}

	public SheetPage(String name) {
		_name=name;
		_timestamp=new Date();
	}
	
	@Lob
	@Column(name=SheetPage.PROP_SERIALIZED_PAGE,nullable=false)
	public String getSerializedPage() {
		return _serializedPage;
	}
	
	@Basic
	@Column(name="timestamp")
	public Date getTimestamp(){
		return _timestamp;
	}

	public void setSerializedPage(String serializedPage) {
		_serializedPage=serializedPage;
		if(isDeserializeRequired.getAndSet(false)){
			deserialiazePage();
		}
	}
	
	@Transient
	public List<SheetContainer> getContainers() {
		return _containers;
	}

	public void setContainers(List<SheetContainer> containers) {
		_containers=containers;
	}
	
	public void setTimestamp(Date date){
		if(date != null) {
			_timestamp=new Date(date.getTime());
		} else {
			_timestamp=new Date();
		}
	}

	/**
	 * @return the name
	 */
	@Basic
	@Column(name=SheetPage.PROP_NAME,length=50,nullable=false)
	public String getName() {
		return _name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		_name = name;
	}

	/**
	 * Add a {@link SheetContainer} in the last position of the parent {@link SheetPage}
	 * 
	 * @return the newly created {@link SheetContainer}
	 */
	public SheetContainer addContainer(int x, int y) {
		SheetContainer container = new SheetContainer(x, y);
		_containers.add(container);
		return container;
	}

	public SheetContainer getContainer(String uid) {
		if(_cachedContainer.containsKey(uid)) {
			return _cachedContainer.get(uid);
		}

		for (Iterator<SheetContainer> iterator = _containers.iterator(); iterator.hasNext();) {
			SheetContainer container = iterator.next();
			if(container.getUid().equals(uid)) {
				_cachedContainer.put(uid, container);
				return container;
			}				
		}
		
		throw new RuntimeException("Container not found");
	}
	
	public void serializePage(){
		String serializedPage;
		
		if(logger.isDebugEnabled()) {
			Gson gson = loadSheetPageGson(true);
			serializedPage = gson.toJson(this);
			logger.debug("       Going to Serialize Page : " + this);
			logger.debug("       Serialiazed data : ");
			logger.debug(serializedPage);
		} else {
			Gson gson = loadSheetPageGson();
			serializedPage = gson.toJson(this);
		}
		
		setSerializedPage(serializedPage);
	}
	
	public final static Gson loadSheetPageGson() {
		return loadSheetPageGson(false);
	}
	
	public final static Gson loadSheetPageGson(boolean debug) {
		GsonBuilder gsonb = new GsonBuilder();
		gsonb.setExclusionStrategies(new SheetExlusionStrategy());
		if(debug){
			gsonb.setPrettyPrinting();
		}
		return gsonb.create();
	}	

	@Override
	public String toString() {
		return "SheetPage [Id="+ getId() + ", Name=" + _name + ", Timestamp=" + _timestamp + ", #Containers=" + _containers.size() + "]";
	}
	
	private void deserialiazePage() {
		Gson gson = SheetPage.loadSheetPageGson();
		
		SheetPage deserializedPage = gson.fromJson(_serializedPage, SheetPage.class);
		
		copyPage(deserializedPage);
	}
	
	private void copyPage(SheetPage deserializedPage) {
		List<SheetContainer> orig = deserializedPage.getContainers();
		if(logger.isDebugEnabled()) {
			logger.debug("Copy SheetPage data : ");
			logger.debug("     Have {} Containers to copy: ", orig.size());
		}
		_containers.clear();
		_containers.addAll(orig);
	}
}
